# -*- coding: utf-8 -*-
"""
sklearn中的PCA使用datasets
Created on Thu Mar 29 14:42:46 2018

@author: Allen
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

digits = datasets.load_digits()
X = digits.data
y = digits.target

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split( X, y )

# 使用knn算法进行分类
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier()
knn_clf.fit( X_train, y_train )
print( knn_clf.score( X_test, y_test ) ) # 0.98

# 使用PCA降维
from sklearn.decomposition import PCA
pca = PCA( n_components = 2 )
pca.fit( X_train )
X_train_reduction = pca.transform( X_train ) 
X_test_reduction = pca.transform( X_test ) # 特别注意：和归一化一样，要用训练集建立模型，来处理测试集

knn_clf1 = KNeighborsClassifier()
knn_clf1.fit( X_train_reduction, y_train )
print( knn_clf1.score( X_test_reduction, y_test ) ) # 0.64

'''
思考：
    可以看出pca降维之后，运算速度有了明显提升，但是准确率降了下来
    如何寻找到一个最佳的维度值，既实现了降维，又令准确率不会降低太多？
'''

'''
可以使用sklearn中提供的一个指标：
    pca.explained_variance_ratio_ ( 解释的方差比例 )
'''
print( pca.explained_variance_ratio_ )
'''
输出 [ 0.15054435  0.13549338]
代表的含义是：
    第一个主成分贡献了15%的方差
    第二个主成分贡献了13%的方差
那么现在的思路是，将所有的轴的explained_variance_ratio_ 打印出来，
看看哪些轴贡献的方差比例大就保留哪些轴。可以人为设定一个方差比例，
看看有多少轴可以达到这个要求，而达到要求的这个轴数，就是需要的n_components
'''
pca = PCA( n_components = X_train.shape[1] )
pca.fit( X_train )
print( pca.explained_variance_ratio_ )
'''
输出
[  1.47388236e-01   1.38632960e-01   1.15503005e-01   8.32673761e-02
   5.77815941e-02   5.00098524e-02   4.40330455e-02   3.70595833e-02
   3.44665272e-02   3.04724672e-02   2.45819594e-02   2.23054954e-02
   1.80170878e-02   1.73610671e-02   1.47659745e-02   1.42150151e-02
   1.31530500e-02   1.20891297e-02   9.99874796e-03   9.24229903e-03
   8.93046536e-03   7.84255677e-03   7.77627358e-03   7.19060965e-03
   6.97124545e-03   5.87327080e-03   5.64372053e-03   5.17946455e-03
   4.95907462e-03   4.38009242e-03   3.83302543e-03   3.52153458e-03
   3.33202980e-03   3.27222326e-03   3.11685192e-03   2.97891535e-03
   2.45740725e-03   2.30356664e-03   2.23369490e-03   2.18582619e-03
   1.86310158e-03   1.57056294e-03   1.51735185e-03   1.42311552e-03
   1.14459888e-03   1.08775867e-03   9.80418187e-04   7.06352076e-04
   5.13554786e-04   4.05810759e-04   2.13348428e-04   7.87488586e-05
   5.28608769e-05   5.18706396e-05   4.19005854e-05   9.79988992e-06
   7.07667833e-06   3.48528034e-06   8.64276652e-07   7.07495358e-07
   3.89860137e-07   6.21063232e-34   6.21063232e-34   5.99301240e-34]
可以看出这些贡献是从大到小排列的，第一个贡献14%，最后一个贡献几乎为0
'''
plt.plot( [i for i in range( X_train.shape[1] )], [ np.sum( pca.explained_variance_ratio_[:i+1] ) for i in range( X_train.shape[1] ) ] )
plt.show()
'''
可以看到一条曲线，纵轴是方差贡献率的和，横轴是维数，
需要90的方差贡献度，查看对应的维数即可
sklearn中已经封装了这个方法
'''
pca = PCA( 0.95 ) # 直接写上 需要的 贡献度
pca.fit( X_train )
print( pca.n_components_ ) # 输出 28，也就是说 28维就已经达到了0.95的贡献度
X_train_reduction = pca.transform( X_train )
X_test_reduction = pca.transform( X_test )

knn_clf2 = KNeighborsClassifier()
knn_clf2.fit( X_train_reduction, y_train )
print( knn_clf2.score( X_test_reduction, y_test ) ) # 0.984444444444
'''
可以看出使用 28 维，精度也能达到98%。
既增加了运算速度，又保证了正确率。
'''
'''
另外，把数据降低到二维，有一个重要的意义就是实现数据的可视化！！
'''